package com.cjhstudy.mall.service.impl;

import com.cjhstudy.mall.dao.ProductMapper;
import com.cjhstudy.mall.enums.ProductStatusEnum;
import com.cjhstudy.mall.form.CartAddForm;
import com.cjhstudy.mall.form.CartUpdateForm;
import com.cjhstudy.mall.pojo.Cart;
import com.cjhstudy.mall.pojo.Product;
import com.cjhstudy.mall.service.ICartService;
import com.cjhstudy.mall.vo.CartProductVo;
import com.cjhstudy.mall.vo.CartVo;
import com.cjhstudy.mall.vo.ResponseVo;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.cjhstudy.mall.enums.ResponseEnum.*;

/**
 * @author: phil
 * @ProjectName: mall
 * @Date: 2021/12/10
 */
@Service
public class CartServiceImpl implements ICartService {

    private final static String CART_REDIS_KEY_TEMPLATE = "cart_%d";

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    private Gson gson = new Gson();

    /**
     * 添加商品信息到购物车列表，这个操作步骤是：你得先把你要添加的商品id 找到然后在数据库中查到，查到之后先将其数据写到redis中，
     * 然后在调用 list()方法返回购物车列表的所有数据
     * @param uid 用户的id
     * @param form 添加购物车的表单信息
     * @return
     */
    @Override
    public ResponseVo<CartVo> add(Integer uid, CartAddForm form) {

        Integer quantity = 1;

        //在数据库中查找 product 商品的对象信息
        Product product = productMapper.selectByPrimaryKey(form.getProductId());

        //商品是否存在
        if (product == null) {
            return ResponseVo.error(PRODUCT_NOT_EXIST);
        }
        //商品是否正常在售
        if (!product.getStatus().equals(ProductStatusEnum.ON_SALE.getCode())) {
            return ResponseVo.error(PRODUCT_OFF_SALE_OR_DELETE);
        }

        //商品库存是否充足
        if (product.getStock() <= 0) {
            return ResponseVo.error(PRODUCT_STOCK_ERROR);
        }

        //写入到 Redis
        //key:cart_1
        //先创建一个Hash存储对象
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        //把redis的key拿出来
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);

        //定义一个新的cart对象，用于存储到redis中
        Cart cart;
        //先将 redis 中对应key的对象取出来
        String value = opsForHash.get(redisKey, String.valueOf(product.getId()));
        //判断value是否存在
        if (StringUtils.isEmpty(value)) {
            //说明没有改商品，新增
            cart = new Cart(product.getId(), quantity, form.getSelected());
        } else {
            //已经有了，数量+1
            cart = gson.fromJson(value, Cart.class); //将json数据转成cart对象
            cart.setQuantity(cart.getQuantity() + quantity);
        }

        /**
         * gson.toJson(cart)): 将对象转换成一个json格式
         */
        opsForHash.put(redisKey,
                String.valueOf(product.getId()),
                gson.toJson(cart));

        return list(uid); //调用list() 方法是为了返回当前的所有购物车中的数据
    }


    /**
     * 查询购物车中的所有商品信息，注意一下这个是已经通过add()方法写进redis中了的，那么我们就直接可以对应着 uid redis中的数据查出，并统计好各项数据
     * @param uid 用户的 uid(userid)
     * @return 购物车的列表信息
     */
    @Override
    public ResponseVo<CartVo> list(Integer uid) {

        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        //把redis的key拿出来
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);

        //把redis中的所有对象找出来
        Map<String, String> entries = opsForHash.entries(redisKey);

        Boolean selectAll = true;
        Integer cartTotalQuantity = 0;
        BigDecimal cartTotalPrice = BigDecimal.ZERO;
        CartVo cartVo = new CartVo();
        List<CartProductVo> cartProductVoList = new ArrayList<>();

        for (Map.Entry<String, String> entry : entries.entrySet()) {
            Integer productId = Integer.valueOf(entry.getKey());
            Cart cart = gson.fromJson(entry.getValue(), Cart.class);

            //TODO  需要优化，使用mysql里的in
            Product product = productMapper.selectByPrimaryKey(productId);
            if (product != null) {
                CartProductVo cartProductVo = new CartProductVo(productId,
                        cart.getQuantity(),
                        product.getName(),
                        product.getSubtitle(),
                        product.getMainImage(),
                        product.getPrice(),
                        product.getStatus(),
                        product.getPrice().multiply(BigDecimal.valueOf(cart.getQuantity())),
                        product.getStock(),
                        cart.getProductSelected()
                );
                cartProductVoList.add(cartProductVo);

                if (!cart.getProductSelected()) {
                    selectAll = false;
                }
                //计算总价（只计算选中的）
                if (cart.getProductSelected()) {
                    cartTotalPrice = cartTotalPrice.add(cartProductVo.getProductTotalPrice());
                }
            }

            cartTotalQuantity += cart.getQuantity();

        }

        //有一个没有选中那就不叫全选
        cartVo.setSelectAll(selectAll);
        cartVo.setCartTotalQuantity(cartTotalQuantity);
        cartVo.setCartTotalPrice(cartTotalPrice);
        cartVo.setCartProductVoList(cartProductVoList);

        return ResponseVo.success(cartVo);
    }


    /**
     * 更新购物车里的商品信息，其实就是更新redis数据库中的信息因为这个所有的商品信息都是存放在redis中的。
     * @param uid 用户id
     * @param productId 要更新的商品id
     * @param form 更新的表单数据
     * @return 返回已更新的商品信息
     */
    @Override
    public ResponseVo<CartVo> update(Integer uid, Integer productId, CartUpdateForm form) {

        HashOperations<String, String, Object> opsForHash = redisTemplate.opsForHash();
        //把redis的key拿出来
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);

        //先将 redis 中对应key的对象取出来
        String value = (String) opsForHash.get(redisKey, String.valueOf(productId));
        //判断value是否存在
        if (StringUtils.isEmpty(value)) {
            //说明没有改商品，报错
            return ResponseVo.error(CART_PRODUCT_NOT_EXIST);
        }
        //已经有了，修改内容
        Cart cart = gson.fromJson(value, Cart.class); //将json数据转成cart对象
        if (form.getQuantity() != null && form.getQuantity() > 0) {
            cart.setQuantity(form.getQuantity());

        }

        if (form.getSelected() != null) {
            cart.setProductSelected(form.getSelected());
        }
        opsForHash.put(redisKey, String.valueOf(productId), gson.toJson(cart));

        return list(uid);

    }


    /**
     * 删除购物车的商品，这个操作还是在redis层面的删除，并不会删除数据库中的信息
     * @param uid 用户登录的id
     * @param productId 要删除的商品id
     * @return 返回删除后的购物车列表
     */
    @Override
    public ResponseVo<CartVo> delete(Integer uid, Integer productId) {

        HashOperations<String, String, Object> opsForHash = redisTemplate.opsForHash();
        //把redis的key拿出来
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);

        //先将 redis 中对应key的对象取出来
        String value = (String) opsForHash.get(redisKey, String.valueOf(productId));
        //判断value是否存在
        if (StringUtils.isEmpty(value)) {
            //说明没有改商品，报错
            return ResponseVo.error(CART_PRODUCT_NOT_EXIST);
        }

        opsForHash.delete(redisKey, String.valueOf(productId));

        return list(uid);
    }


    /**
     * 购物车的全选操作，基本思想就是将redis中的ProductSelected置为true，然后返回列表信息
     * @param uid 用户id
     * @return 返回全选的列表信息
     */
    @Override
    public ResponseVo<CartVo> selectAll(Integer uid) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        //把redis的key拿出来
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);

        for (Cart cart : listForCart(uid)) {
            cart.setProductSelected(true);
            opsForHash.put(redisKey,String.valueOf(cart.getProductId()),gson.toJson(cart));
        }
        return list(uid);
    }


    /**
     * 购物车中的商品全不选，基本思想就是将redis中的ProductSelected置为false，然后返回列表信息
     * @param uid 用户id
     * @return  返回用户的全部选的商品列表
     */
    @Override
    public ResponseVo<CartVo> unSelectAll(Integer uid) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        //把redis的key拿出来
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);

        for (Cart cart : listForCart(uid)) {
            cart.setProductSelected(false);
            opsForHash.put(redisKey,String.valueOf(cart.getProductId()),gson.toJson(cart));
        }
        return list(uid);
    }

    /**
     * 统计商品的所有数量信息
     * @param uid 用户id
     * @return 返回所有信息的总和
     */
    @Override
    public ResponseVo<Integer> sum(Integer uid) {
        Integer sum = listForCart(uid).stream()
                .map(Cart::getQuantity)
                .reduce(0,Integer::sum);
        return ResponseVo.success(sum);
    }


    /**
     * selectAll / unSelectAll 中的方法调用，就是查询redis操作返回 相应的对象信息
     * @param uid 用户id
     * @return 返回用户购物车信息
     */
    @Override
    public List<Cart> listForCart(Integer uid){
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        //把redis的key拿出来
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);

        //把redis中的所有对象找出来
        Map<String, String> entries = opsForHash.entries(redisKey);

        List<Cart> cartList = new ArrayList<>();
        for (Map.Entry<String, String> entry : entries.entrySet()) {
            cartList.add(gson.fromJson(entry.getValue(),Cart.class));
        }

        return cartList;
    }
}
